package cn.powertime.evaluation.web.controller;

import cn.powertime.evaluation.entity.SysUser;
import cn.powertime.evaluation.enums.HttpStatus;
import cn.powertime.evaluation.exception.LoginException;
import cn.powertime.evaluation.facade.SysUserFacade;
import cn.powertime.evaluation.logging.EnumLogType;
import cn.powertime.evaluation.logging.Logging;
import cn.powertime.evaluation.shiro.jwt.JwtToken;
import cn.powertime.evaluation.shiro.jwt.SubjectUtil;
import cn.powertime.evaluation.shiro.jwt.TokenUtils;
import cn.powertime.evaluation.util.PasswordHelper;
import cn.powertime.evaluation.vo.ReqLoginUserVo;
import cn.powertime.evaluation.vo.RespLoginUserVo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.subject.Subject;
import org.apache.tomcat.util.codec.binary.Base64;
import org.orclight.java.util.captha.CaptchaClient;
import org.orclight.java.util.captha.bean.CaptchaBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  用户登录Controller
 * </p>
 *
 * @author ZYW
 * @version v1.0.0
 * @date 2019-09-02 16:41
 */
@RestController
@RequestMapping("/auth")
@Api(value = "/auth", tags = "用户登录", produces = MediaType.ALL_VALUE)
public class LoginController extends BaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private TokenUtils tokenUtils;

    @Autowired
    private SubjectUtil subjectUtil;

    @Autowired
    private SysUserFacade userFacade;

    @Autowired
    private CaptchaClient captchaClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping(value="/captcha")
    @ApiOperation(value = "验证账号唯一性", notes = "")
    public Object captcha(){
        LOGGER.info("生成验证码");
        try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            CaptchaBean captchaBean = captchaClient.generate();
            String capText = captchaBean.getResult();
            String uuid = UUID.randomUUID().toString();
            redisTemplate.boundValueOps(uuid).set(capText,60, TimeUnit.SECONDS);
            BufferedImage bi = captchaBean.getBufferedImage();
            ImageIO.write(bi, "jpg", baos);
            String imgBase64 = Base64.encodeBase64String(baos.toByteArray());

            return ResponseEntity.ok()
                    .header("Access-Control-Allow-Origin","*")
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(
                            ImmutableMap.of("data", ImmutableMap.of(uuid,"data:image/jpeg;base64,"+imgBase64)
                                    ,"code", 200
                                    ,"msg", "成功！"
                                    ,"timestamp", System.currentTimeMillis())
                    );
        } catch (IOException e) {
            return error("生成验证码失败");
        }
    }

    @PostMapping(value = "/login")
    @ApiOperation(value = "用户登录", notes = "")
    @Logging(code = "user.login",vars = {"loginUser.username","loginUser"},type = EnumLogType.ADD)
    public Object login(@Validated @RequestBody ReqLoginUserVo loginUser, HttpServletResponse response){

        //检查验证码
        //checkedCode(loginUser);

        // 根据用户名查询用户
        List<SysUser> userList = userFacade.selectByAccount(loginUser.getUsername());
        if(CollectionUtils.isEmpty(userList)){
            return error("用户不存在！");
        }

        if(userList.size() != 1) {
            return error("用户不唯一！");
        }

        SysUser user = userList.get(0);

        // 验证用户状态
        if(user.getStatus() != 1){
            return error("用户已禁用！");
        }

        String pwd = PasswordHelper.builder().build().encrypt(loginUser.getPassword(), String.valueOf(user.getId()), user.getPwdSalt());
        // 验证密码
        if(!StringUtils.equalsIgnoreCase(pwd,user.getPassword())){
            return error("密码错误！");
        }

        // 验证用户名密码成功后生成token
        String token = tokenUtils.generateToken(loginUser.getUsername(),user.getId());
        JwtToken jwtToken = JwtToken.builder().token(token).principal(loginUser.getUsername()).build();

        Subject subject = SecurityUtils.getSubject();

        subject.login(jwtToken);

        // 认证成功后把token写出到Cookie
        if (subject.isAuthenticated()){
            Cookie cookie = new Cookie("token",token);
            cookie.setHttpOnly(true);
            cookie.setMaxAge(3600 * 5);
            cookie.setPath("/");
            response.addCookie(cookie);
        }

        return success();
    }

    /**
     * 验证验证码输入
     * @param loginUser
     */
    private void checkedCode(ReqLoginUserVo loginUser) {
        Object val = redisTemplate.boundValueOps(loginUser.getVcodeKey()).get();
        if(val == null) {
            throw new LoginException("验证码已过期，请刷新重试！");
        }
        String vcode = String.valueOf(val);
        if(!StringUtils.equalsIgnoreCase(loginUser.getVcode(),vcode)) {
            throw new LoginException("验证码输入错误！");
        }
    }

    @GetMapping(value = "/islogin")
    @ApiOperation(value = "用户是否登录", notes = "")
    public Object isLogin(HttpServletRequest request){
        String token = TokenUtils.getRequestToken(request);
        if(StringUtils.isBlank(token)){
            return responseEntity(HttpStatus.CHECK_FAIL,null,"令牌为空！");
        }
        SysUser user = subjectUtil.getSubjectIdFromToken(token);

        if(user == null){
            return error("用户不存在！");
        }
        RespLoginUserVo respLoginUserVo = new RespLoginUserVo();
        BeanUtils.copyProperties(user,respLoginUserVo);

        return success(respLoginUserVo);
    }


    @GetMapping(value = "/logout")
    @ApiOperation(value = "退出登录", notes = "")
    public Object logout(HttpServletRequest request,HttpServletResponse response) throws IOException {
        Optional<Cookie> cookie = Arrays.stream(request.getCookies())
                .filter(ck -> "token".equals(ck.getName()))
                .limit(1)
                .map(ck -> {
                    ck.setMaxAge(0);
                    ck.setHttpOnly(true);
                    ck.setPath("/");
                    return ck;
                })
                .findFirst();
        response.addCookie(cookie.get());
        response.flushBuffer();
        return success();
    }


    /**
     * 刷新token
     * @param token token
     * @return 新token
     */
    @GetMapping("/token/refresh")
    @RequiresAuthentication
    @ApiOperation(value = "刷新token", notes = "")
    public Object refreshToken(@CookieValue("token") String token) {
        String newToken = this.tokenUtils.refreshToken(token);
        HashMap<Object, Object> ret = Maps.newHashMap();
        ret.put("token", newToken);
        return success(ret);
    }
}
